package NIO;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

/**
 * @author: lsx
 * @description: nio服务端
 * @date: 2020/3/2
 */
public class NioServer {
    /**
     * 通道管理器
     */
    private Selector selector;

    /**
     * 绑定端口号
     * @param port
     * @throws IOException
     */
    public void initServer(int port) throws IOException {
        //获取一个serversocket通道
        ServerSocketChannel socketChannel = ServerSocketChannel.open();
        //设置通道为非阻塞
        socketChannel.configureBlocking(false);
        //将通道对应的ServerSocket绑定到port端口
        socketChannel.socket().bind(new InetSocketAddress(port));
        //获取通道管理器
        this.selector = Selector.open();
       /*将通道管理器与通道绑定，并给该通道注册SelectionKey.OP_ACCEPT事件，注册后，
        当事件达到时，selector.select()会返回，如果事件没有达到selector.select()会一直阻塞*/
        socketChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    /**
     * 采用轮询的方式监听selector上是否有需要处理的事件，如果有则进行处理
     * @throws IOException
     */
    public void listen() throws IOException {
        System.out.println("服务端启动成功");
        // 轮询访问selector
        while (true){
            //当时事件达到时，方法返回，否则，该方法一直被阻塞；
            selector.select();
            //获取selector中选择项的迭代器，选中项为注册事件
            Iterator<SelectionKey> iterator = this.selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                SelectionKey selectionKey = iterator.next();
                //删除已选中的key，防止重复处理
                iterator.remove();
                handler(selectionKey);
            }
        }
    }

    /**
     * 处理请求
     * @param selectionKey
     */
    public void handler(SelectionKey selectionKey) throws IOException {
        if(selectionKey.isAcceptable()){
            //客户端请求连接事件
            handlerAccept(selectionKey);
        }else if(selectionKey.isReadable()){
            //获取可读事件
            handlerRead(selectionKey);
        }
    }

    /**
     * 处理连接请求
     * @param selectionKey
     */
    private void handlerAccept(SelectionKey selectionKey) throws IOException {
        ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();
        //获取客户端连接通道
        SocketChannel channel = server.accept();
        //设置为非阻塞
        channel.configureBlocking(false);
        //在此给客户端发送消息
        System.out.println("新的客户端连接---->>>>>>>>>");
        //客户端连接成功以后，为接受客户端消息，给通道设置读取权限
        channel.register(this.selector,SelectionKey.OP_READ);
    }

    /**
     * 处理读的事件
     * @param selectionKey
     * @throws IOException
     */
    private void handlerRead(SelectionKey selectionKey) throws IOException {
        //得到事件发生的socket通道
        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
        //创建读取的缓存区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int read = socketChannel.read(buffer);
        if(read>0){
            byte[] data = buffer.array();
            String msg = new String(data).trim();
            System.out.println("服务端收到的消息是："+msg);

            //回写数据
            String info = "回复一下";
            ByteBuffer outBuffer = ByteBuffer.wrap(info.getBytes());
            //将消息回发服务端
            socketChannel.write(outBuffer);
        }else {
            System.out.println("客户端关闭!");
            selectionKey.cancel();
        }
    }

    /**
     * 启动服务端测试
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        NioServer server = new NioServer();
        server.initServer(8088);
        server.listen();
    }
}
